/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package etherblocks.engine.states;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AppStateManager;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import etherblocks.engine.*;
import etherblocks.engine.gui.*;
import etherblocks.engine.network.messages.*;
import etherblocks.game.blocks.*;
import etherblocks.mods.*;
import etherblocks.engine.objects.ModelObject;
import etherblocks.texturepacks.packs.*;
import com.cubes.BlockManager;
import com.cubes.Vector3Int;

/**
 *
 * @author Carl
 */
public class PlayerAppState extends BaseAppState implements ActionListener{

    public PlayerAppState(){
        
    }
    public static final Vector3f HITBOX = new Vector3f(0.66f, 1.25f, 0.66f);
    private ModelObject playerModel;
    private CharacterControl playerControl;
    private Vector3f walkDirection = new Vector3f();
    private Vector3f viewDirection = new Vector3f();
    private boolean[] arrowKeys = new boolean[4];
    private boolean isRunning;
    private long lastStateUpdateTimestamp;
    private Class[] blocksToSetClasses = new Class[]{
        Block_Grass.class,
        Block_Stone.class,
        Block_Trunk.class,
        Block_Wood.class,
        Block_Brick.class,
        Block_Snow.class,
        Block_Lava.class,
        Block_Diamond.class,
        Block_Sand.class,
        Block_Rock.class,
        Block_Glass.class,
        Block_WoodWindow.class,
        Block_Stonebrick.class,
        Block_Sandstone.class,
        Block_Hopper.class,
        Block_Cactus.class,
        Block_Melon.class
    };
    private int selectionStartItemIndex = 0;
    private int selectedItemIndex = 0;
    
    @Override
    public void initialize(AppStateManager stateManager, Application application){
        super.initialize(stateManager, application);
        initControls();
        initGUI();
    }
    
    private void initControls(){
        Mod mod = getAppState(ModAppState.class).getMod();
        mainApplication.getInputManager().deleteMapping("FLYCAM_ZoomIn");
        mainApplication.getInputManager().deleteMapping("FLYCAM_ZoomOut");
        mainApplication.getInputManager().deleteMapping(SimpleApplication.INPUT_MAPPING_EXIT);
        mainApplication.getInputManager().addMapping("move_up", new KeyTrigger(KeyInput.KEY_W));
        mainApplication.getInputManager().addMapping("move_right", new KeyTrigger(KeyInput.KEY_D));
        mainApplication.getInputManager().addMapping("move_down", new KeyTrigger(KeyInput.KEY_S));
        mainApplication.getInputManager().addMapping("move_left", new KeyTrigger(KeyInput.KEY_A));
        mainApplication.getInputManager().addMapping("run", new KeyTrigger(KeyInput.KEY_LSHIFT));
        mainApplication.getInputManager().addMapping("jump", new KeyTrigger(KeyInput.KEY_SPACE));
        if(mod.getProperties().getBoolean("isBlockTerrainEditable")){
            mainApplication.getInputManager().addMapping("set_block", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
            mainApplication.getInputManager().addMapping("remove_block", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
        }
        mainApplication.getInputManager().addMapping("item_previous", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));
        mainApplication.getInputManager().addMapping("item_next", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true));
        if(mod.getProperties().getBoolean("isRespawnAllowed")){
            mainApplication.getInputManager().addMapping("respawn", new KeyTrigger(KeyInput.KEY_BACK));
        }
        mainApplication.getInputManager().addMapping("change_block_material_type", new KeyTrigger(KeyInput.KEY_P));
        mainApplication.getInputManager().addMapping("skin_1", new KeyTrigger(KeyInput.KEY_1));
        mainApplication.getInputManager().addMapping("skin_2", new KeyTrigger(KeyInput.KEY_2));
        mainApplication.getInputManager().addMapping("skin_3", new KeyTrigger(KeyInput.KEY_3));
        mainApplication.getInputManager().addMapping("skin_4", new KeyTrigger(KeyInput.KEY_4));
        mainApplication.getInputManager().addMapping("skin_5", new KeyTrigger(KeyInput.KEY_5));
        if(mod.getProperties().getBoolean("isCameraChangable")){
            mainApplication.getInputManager().addMapping("camera_first_person", new KeyTrigger(KeyInput.KEY_NUMPAD1));
            mainApplication.getInputManager().addMapping("camera_third_person", new KeyTrigger(KeyInput.KEY_NUMPAD3));
        }
        //mainApplication.getInputManager().addMapping("pause_menu", new KeyTrigger(KeyInput.KEY_ESCAPE));
        mainApplication.getInputManager().addListener(this, new String[]{
            "move_up","move_right","move_down","move_left","run","jump","set_block","remove_block","respawn","item_previous","item_next","change_block_material_type","skin_1","skin_2","skin_3","skin_4","skin_5","camera_first_person","camera_third_person","pause_menu"
        });
    }
    
    private void initGUI(){
        Mod mod = getAppState(ModAppState.class).getMod();
        NiftyAppState niftyAppState = getAppState(NiftyAppState.class);
        niftyAppState.createNifty("Interface/IngameMenu.xml");
        ScreenController_IngameMenu screenController_IngameMenu = niftyAppState.getScreenController(ScreenController_IngameMenu.class);
        if(mod.getProperties().getBoolean("isItemBarVisible")){
            updateDisplayedItems();
        }
        else{
            screenController_IngameMenu.setItemBarVisible(false);
        }
        screenController_IngameMenu.setCrosshairVisible(mod.getProperties().getBoolean("isCrosshairVisible"));
    }
    
    public void updateDisplayedItems(){
        NiftyAppState niftyAppState = getAppState(NiftyAppState.class);
        ScreenController_IngameMenu screenController_IngameMenu = niftyAppState.getScreenController(ScreenController_IngameMenu.class);
        for(int i=0;i<8;i++){
            screenController_IngameMenu.setItem(i, BlockManager.getType(blocksToSetClasses[selectionStartItemIndex + i]));
        }
    }

    public void onPlayerModelChanged(){
        boolean isInitialized = (playerModel == null);
        int playerEntityID = getAppState(NetworkAppState.class).getNetworkClient().getGameClient().getPlayerEntityID();
        playerModel = (ModelObject) getAppState(EntitySystemAppState.class).getSpatial(playerEntityID);
        playerControl = playerModel.getControl(CharacterControl.class);
        playerControl.setJumpSpeed(13);
        playerControl.setFallSpeed(15);
        if(isInitialized){
            mainApplication.getCamera().lookAtDirection(new Vector3f(1, 0, 1), Vector3f.UNIT_Y);
            respawn();
            mainApplication.getStateManager().attach(new PlayerCameraAppState());
            mainApplication.getStateManager().attach(new GameStartedAppState());
        }
        else{
            getAppState(PlayerCameraAppState.class).onPlayerModelChanged();
        }
    }
    
    public void respawn(){
        playerControl.setPhysicsLocation(new Vector3f(5, 35, 5).mult(getBlockSize()));
    }

    @Override
    public void update(float lastTimePerFrame){
        super.update(lastTimePerFrame);
        float playerSpeed = (isRunning?0.35f:0.2f);
        Camera camera = mainApplication.getCamera();
        Vector3f cameraDir = camera.getDirection();
        Vector3f cameraLeft = camera.getLeft();
        walkDirection.set(0, 0, 0);
        if(arrowKeys[0]){ walkDirection.addLocal(cameraDir); }
        if(arrowKeys[1]){ walkDirection.addLocal(cameraLeft.negate()); }
        if(arrowKeys[2]){ walkDirection.addLocal(cameraDir.negate()); }
        if(arrowKeys[3]){ walkDirection.addLocal(cameraLeft); }
        walkDirection.setY(0).normalizeLocal().multLocal(playerSpeed);
        playerControl.setWalkDirection(walkDirection);
        boolean isMoving = (walkDirection.length() > 0);
        if(getAppState(PlayerCameraAppState.class).getCameraType() == PlayerCameraAppState.CameraType.FIRST_PERSON){
            viewDirection.set(camera.getDirection()).setY(0);
        }
        else if(isMoving){
            viewDirection.set(walkDirection);
        }
        playerControl.setViewDirection(viewDirection);
        if(Util.isTimeElapsed(lastStateUpdateTimestamp, 33)){
            NetworkAppState networkAppState = getAppState(NetworkAppState.class);
            networkAppState.getNetworkClient().sendMessage(new Message_PlayerState(networkAppState.getNetworkClient().getID(), playerControl.getPhysicsLocation(), viewDirection, isMoving));
            lastStateUpdateTimestamp = System.currentTimeMillis();
        }
    }

    public void onAction(String actionName, boolean value, float lastTimePerFrame){
        NetworkAppState networkAppState = getAppState(NetworkAppState.class);
        WorldAppState worldAppState = getAppState(WorldAppState.class);
        NiftyAppState niftyAppState = getAppState(NiftyAppState.class);
        PlayerCameraAppState playerCameraAppState = getAppState(PlayerCameraAppState.class);
        if(actionName.equals("move_up")){
            arrowKeys[0] = value;
        }
        else if(actionName.equals("move_right")){
            arrowKeys[1] = value;
        }
        else if(actionName.equals("move_down")){
            arrowKeys[2] = value;
        }
        else if(actionName.equals("move_left")){
            arrowKeys[3] = value;
        }
        else if(actionName.equals("run")){
            isRunning = value;
        }
        else if(actionName.equals("jump") && value){
            playerControl.jump();
        }
        else if(actionName.equals("respawn") && value){
            respawn();
        }
        else if(actionName.equals("change_block_material_type") && value){
            getAppState(TexturePackAppState.class).changeBlockMaterialType();
        }
        else if(actionName.equals("skin_1") && value){
            getAppState(TexturePackAppState.class).setTexturePack(new DefaultTexturePack(1));
        }
        else if(actionName.equals("skin_2") && value){
            getAppState(TexturePackAppState.class).setTexturePack(new DefaultTexturePack(2));
        }
        else if(actionName.equals("skin_3") && value){
            getAppState(TexturePackAppState.class).setTexturePack(new DefaultTexturePack(3));
        }
        else if(actionName.equals("skin_4") && value){
            getAppState(TexturePackAppState.class).setTexturePack(new DefaultTexturePack(4));
        }
        else if(actionName.equals("skin_5") && value){
            getAppState(TexturePackAppState.class).setTexturePack(new GhostTexturePack());
        }
        else if(actionName.equals("camera_first_person") && value){
            getAppState(PlayerCameraAppState.class).setCameraType(PlayerCameraAppState.CameraType.FIRST_PERSON);
        }
        else if(actionName.equals("camera_third_person") && value){
            getAppState(PlayerCameraAppState.class).setCameraType(PlayerCameraAppState.CameraType.THIRD_PERSON);
        }
        else if(actionName.equals("pause_menu") && value){
            getAppState(PlayerCameraAppState.class).setCameraType(PlayerCameraAppState.CameraType.THIRD_PERSON);
            niftyAppState.goToScreen(ScreenController_PauseMenu.class, "menu");
            niftyAppState.getScreenController(ScreenController_IngameMenu.class).setBlockToolsVisible(false);
        }
        else{
            switch(playerCameraAppState.getCameraType()){
                case FIRST_PERSON:
                    if(actionName.equals("set_block") && value){
                        Vector3Int blockLocation = worldAppState.getCurrentPointedBlockLocation(true);
                        if(blockLocation != null){
                            Class blockClass = blocksToSetClasses[selectedItemIndex];
                            networkAppState.getNetworkClient().sendMessage(new Message_BlockSetCommand(blockLocation.getX(), blockLocation.getY(), blockLocation.getZ(), BlockManager.getType(blockClass).getType()));
                        }
                    }
                    else if(actionName.equals("remove_block") && value){
                        Vector3Int blockLocation = worldAppState.getCurrentPointedBlockLocation(false);
                        if((blockLocation != null) && (blockLocation.getY() > 0)){
                            networkAppState.getNetworkClient().sendMessage(new Message_BlockRemoveCommand(blockLocation.getX(), blockLocation.getY(), blockLocation.getZ()));
                        }
                    }
                    else if(actionName.equals("item_previous") && value){
                        boolean wasSelectionStart = ((selectedItemIndex - selectionStartItemIndex) == 0);
                        if(selectedItemIndex > 0){
                            selectedItemIndex--;
                            if(wasSelectionStart){
                                selectionStartItemIndex--;
                                updateDisplayedItems();
                            }
                            niftyAppState.getScreenController(ScreenController_IngameMenu.class).selectItem(selectedItemIndex - selectionStartItemIndex);
                        }
                    }
                    else if(actionName.equals("item_next") && value){
                        boolean wasSelectionEnd = ((selectedItemIndex - selectionStartItemIndex) == 7);
                        if(selectedItemIndex < (blocksToSetClasses.length - 1)){
                            selectedItemIndex++;
                            if(wasSelectionEnd){
                                selectionStartItemIndex++;
                                updateDisplayedItems();
                            }
                            niftyAppState.getScreenController(ScreenController_IngameMenu.class).selectItem(selectedItemIndex - selectionStartItemIndex);
                        }
                    }
                    break;
                
                case THIRD_PERSON:
                    break;
            }
        }
    }
    
    private float getBlockSize(){
        WorldAppState worldAppState = getAppState(WorldAppState.class);
        return worldAppState.getWorld().getBlockTerrain().getSettings().getBlockSize();
    }

    public ModelObject getPlayerModel(){
        return playerModel;
    }

    public CharacterControl getPlayerControl(){
        return playerControl;
    }
}
